package org.opens.config;

import cn.hutool.core.util.ReflectUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;

import java.lang.reflect.Method;
import java.time.Duration;

/**
 * redis缓存的配置
 */
@Configuration
@Slf4j
@ConditionalOnProperty(prefix = "spring.cache",name = "type",havingValue = "redis")
public class RedisConfig extends CachingConfigurerSupport{

    private static final Logger LOG = LoggerFactory.getLogger(RedisConfig.class);

    @Value("${spring.redis.host}")
    private  String host;

    @Value("${spring.redis.port}")
    private  int port;

    @Value("${spring.redis.timeout}")
    private  int timeout;

    @Value("${spring.redis.jedis.pool.max-idle}")
    private int maxIdle;

    @Value("${spring.redis.jedis.pool.max-active}")
    private int maxActive;

    @Value("${spring.redis.jedis.pool.max-wait}")
    private long maxWaitMillis;

    @Autowired
    private JedisConnectionFactory jedisConnectionFactory;

    /**
     * 自定义redis的key生成规则, 在使用注解配置缓存时生效.
     * 生成key如下: org.opens.config.RedisConfig#keyGenerator([1,"str",{"id":1,"name":"2"}])
     * @return  key自动生成器
     */
    @Override
    public KeyGenerator keyGenerator() {
        return (Object target, Method method, Object... params) -> {
            StringBuilder allKeyBuilder = new StringBuilder();

            // 默认情况下以参数的json形式为key
            String paramKey = null;
            if (params.length == 0) {
                paramKey = "";
            } else if (params.length == 1) {
                // 如果函数的参数列表只有一个参数且包含id属性， 则以id为key
                Object idValue = ReflectUtil.getFieldValue(params[0], "id");
                if (idValue != null) {
                    // 为了兼容基本类型，写成这样，因为基本类型序列化为json会变换：1->[1]
                    paramKey = JSON.toJSONString(idValue);
                } else {
                    // 不包含id则以json序列化后的串为key
                    paramKey = JSON.toJSONString(params[0]);
                }
            } else {
                paramKey = JSON.toJSONString(params);
            }

            allKeyBuilder.append(target.getClass().getName())
                    .append("(")
                    .append(paramKey)
                    .append(")");

            return allKeyBuilder.toString();
        };
    }

    private RedisSerializer<String> keySerializer() {
        return new StringRedisSerializer();
    }

    private RedisSerializer<Object> valueSerializer() {
        // return new JdkSerializationRedisSerializer()
        // return new GenericJackson2JsonRedisSerializer();
        return new FastJson2JsonRedisSerializer(Object.class);
    }

    @Override
    public CacheManager cacheManager() {
        //缓存配置对象
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                // 设置缓存的默认超时时间：30分钟
                .entryTtl(Duration.ofMinutes(30L))
                // 如果是空值，不缓存
                .disableCachingNullValues()
                // 设置key序列化器
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
                // 设置value序列化器
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer((valueSerializer())));

        RedisCacheManager build = RedisCacheManager.RedisCacheManagerBuilder
                .fromConnectionFactory(jedisConnectionFactory)
                .cacheDefaults(redisCacheConfiguration)
                .build();

        return build;
    }

    @Bean
    public JedisConnectionFactory jedisConnectionFactory() {
        log.info("使用redis作为缓存");

        // redis连接配置
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(maxActive);
        config.setMaxIdle(maxIdle);
        config.setMaxWaitMillis(maxWaitMillis);

        // redis连接工厂
        JedisConnectionFactory factory = new JedisConnectionFactory(config);
        factory.setHostName(host);
        factory.setPort(port);

        LOG.info("创建JedisConnectionFactory成功！");

        return factory;
    }

    /**
     * 功能:
     *      获取缓存操作助手对象
     * @return  StringRedisTemplate对象, 可以用这个对象来实现操作redis
     */
    @Bean
    public RedisTemplate<String, String> redisTemplate() {
        // 创建Redis缓存操作助手RedisTemplate对象
        StringRedisTemplate template = new StringRedisTemplate();
        // 指定连接工厂
        template.setConnectionFactory(jedisConnectionFactory);
        // key序列化器
        template.setKeySerializer(new StringRedisSerializer());
        // value序列化器
        template.setValueSerializer(new FastJson2JsonRedisSerializer<>(Object.class));
        // hashkey的序列化器
        template.setHashKeySerializer(new StringRedisSerializer());
        // hashkey的值序列化器
        template.setHashValueSerializer(new FastJson2JsonRedisSerializer<>(Object.class));
        return template;
    }

}
